using System;
using System.Collections.Generic;

namespace Measurements
{   
    /// <summary>
    /// Unit of length.
    /// </summary>
    public class LengthUnit : MeasurementUnit
    {   
        /// <summary>
        /// Initializes a new instance of the <see cref="Measurements.LengthUnit"/> class.
        /// </summary>
        /// <param name='name'>
        /// The name of the unit.
        /// </param>
        /// <param name='unit'>
        /// The abbreviation of the unit.
        /// </param>
        /// <param name='toBaseUnit'>
        /// The delegate that converts a value in this unit to a value in its base unit.
        /// </param>
        /// <param name='fromBaseUnit'>
        /// The delegate that converts a value in this unit's base unit to a value in this unit.
        /// </param>
        public LengthUnit(string name, string unit, ToBaseUnitDelegate toBaseUnit, FromBaseUnitDelegate fromBaseUnit)
            : base (name, unit, unit == "m" ? null : Length.Meter, toBaseUnit, fromBaseUnit) { }
    }
 
    /// <summary>
    /// A unit of length deriving directly from the base SI unit.
    /// </summary>
    public class SiLengthUnit : LengthUnit
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="Measurements.SiLengthUnit"/> class.
        /// </summary>
        /// <param name='name'>
        /// The name of the unit.
        /// </param>
        /// <param name='unit'>
        /// The abbreviation of the unit.
        /// </param>
        /// <param name='factor'>
        /// The factor used to convert between this unit and the base SI unit.
        /// </param>
        public SiLengthUnit(string name, string unit, double factor)
            : base(name, unit, m => m * factor, m => m / factor) { }
    }    
 
    /// <summary>
    /// A measurement of length.
    /// </summary>
    public class Length : Measurement<Length, LengthUnit>
    {   
        /// <summary>
        /// The meter, the SI base unit of length.
        /// </summary>
        public static readonly LengthUnit Meter = new LengthUnit("meter", "m", m => m, m => m);
        
        /// <summary>
        /// The kilometer, one-thousand meters.
        /// </summary>
        public static readonly LengthUnit Kilometer = new SiLengthUnit("kilometer", "km", 1e3);
        
        /// <summary>
        /// The centimeter, one one-hundreth of a meter.
        /// </summary>
        public static readonly LengthUnit Centimeter = new SiLengthUnit("centimeter", "cm", 1e-2);
        
        /// <summary>
        /// The millimeter, one one-thousandth of a meter.
        /// </summary>
        public static readonly LengthUnit Millimeter = new SiLengthUnit("millimeter", "mm", 1e-3);
        
        /// <summary>
        /// The inch, one one-twelfth of a foot.
        /// </summary>
        public static readonly LengthUnit Inch = new LengthUnit("inch", "in", m => m * 0.0254, m => m / 0.0254);
        
        /// <summary>
        /// The foot.
        /// </summary>
        public static readonly LengthUnit Foot = new LengthUnit("foot", "ft", m => m * 0.3048, m => m / 0.3048);
        
        /// <summary>
        /// The yard, 3 feet.
        /// </summary>
        public static readonly LengthUnit Yard = new LengthUnit("yard", "yd", m => m * 0.9144, m => m / 0.9144 );
        
        /// <summary>
        /// The mile, 5280 feet.
        /// </summary>
        public static readonly LengthUnit Mile = new LengthUnit("mile", "mi", m => m * 1609.344, m => m / 1609.344);
        
        /// <summary>
        /// The thou, or mil, one one-thousandth of an inche.
        /// </summary>
        public static readonly LengthUnit Thou = new LengthUnit("thou", "thou", m => m * 2.54e-5, m => m / 2.54e-5);
        
        /// <summary>
        /// The light year, the distance light travels in a year.
        /// </summary>
        public static readonly LengthUnit LightYear = new LengthUnit("lightyear", "ly", m => m * 9.4605284e15, m => m / 9.4605284e15);       

        /// <summary>
        /// Common Earth measurements.
        /// </summary>
        public static class Earth
        {
            /// <summary>
            /// The average radius of Earth.
            /// </summary>
            public static Length Radius { get { return Length.Create(6371, Length.Kilometer); } }
            
            /// <summary>
            /// The average circumference of Earth.
            /// </summary>
            public static Length Circumference { get { return Length.Create(40075.16, Length.Kilometer); } }
        }
        
        /// <summary>
        /// Returns a <see cref="System.String"/> that represents the current <see cref="Measurements.Length"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents the current <see cref="Measurements.Length"/>.
        /// </returns>
        public override string ToString()
        {
            return ToString("0.000000", Length.Meter);
        }
    }
}
